using System.Collections.Generic;
using FadeLang.Ast;
using FadeLang.Runtime;

namespace FadeLang.CstVisitor
{
    public class StatementVisitor : FadeLangBaseVisitor<Statement>
    {
        private readonly Context context;

        public StatementVisitor(Context context) {
            this.context = context;
        }

        public override Statement VisitReturn(FadeLangParser.ReturnContext context) {
            var exp = context.exp().Accept(new ExpVisitor(this.context));
            return new ReturnStatement {Value = exp};
        }

        public override Statement VisitDeclarationStat(FadeLangParser.DeclarationStatContext context) {
            var name = context.NAME().GetText();
            var value = context.exp().Accept(new ExpVisitor(this.context));
            return new DeclarationStatement(name, value);
        }

        public override Statement VisitAssignment(FadeLangParser.AssignmentContext context) {
            return new Assignment {
                Name = context.NAME().GetText(),
                Op = AssignmentOpTypeUtils.GetAssignmentOpType(context.op.Text),
                Value = context.exp().Accept(new ExpVisitor(this.context))
            };
        }

        public override Statement VisitFunctiondef(FadeLangParser.FunctiondefContext context) {
            var name = context.NAME().GetText();
            var block = context.funcbody().block().Accept(new BlockVisitor(this.context));
            var arguments = context.funcbody().namelist()?
                .Accept(NameListVisitor.Instance) ?? new List<string>();
            return new FunctionDef {FunctionBlock = block, Name = name, Arguments = arguments};
        }

        public override Statement VisitIf(FadeLangParser.IfContext context) {
            var ifstat = new IfStatement {
                Condition = context.exp().Accept(new ExpVisitor(this.context)),
                TrueBlock = context.block(0)?.Accept(new BlockVisitor(this.context)),
                ElseBlock = context.block(1)?.Accept(new BlockVisitor(this.context))
            };
            if (context.elifList() != null) {
                ifstat.ElseIfStatements = new List<IfStatement>();
                foreach (var elifContext in context.elifList().elif()) {
                    var ifStatement = new IfStatement {
                        Condition = elifContext.exp().Accept(new ExpVisitor(this.context)),
                        TrueBlock = elifContext.block()?.Accept(new BlockVisitor(this.context))
                    };
                    ifstat.ElseIfStatements.Add(ifStatement);
                }
            }
            return ifstat;
        }

        public override Statement VisitFor(FadeLangParser.ForContext context) {
            var forstat = new ForStatement {
                DeclarationStatement = context.declarationStat().Accept(this) as DeclarationStatement,
                Block = context.block().Accept(new BlockVisitor(this.context)),
                Condition = context.exp().Accept(new ExpVisitor(this.context)),
                Assignment = context.assignment().Accept(this) as Assignment
            };
            return forstat;
        }

        public override Statement VisitStat(FadeLangParser.StatContext context) {
            if (context.declarationStat() != null) {
                return context.declarationStat().Accept(this);
            }
            if (context.assignment() != null) {
                return context.assignment().Accept(this);
            }
            if (context.@for() != null) {
                return context.@for().Accept(this);
            }
            if (context.functiondef() != null) {
                return context.functiondef().Accept(this);
            }
            if (context.functionCall() != null) {
                return context.functionCall().Accept(new FunctionCallVisitor(this.context));
            }
            if (context.@if() != null) {
                return context.@if().Accept(this);
            }
            if (context.imports() != null) {
                return context.imports().Accept(this);
            }
            return base.VisitStat(context);
        }

        public override Statement VisitImports(FadeLangParser.ImportsContext context) {
            return new ImportStatement {
                ModulePath = context.@string().Accept(StringVisitor.Instance)
            };
        }
    }
}